Healthcare: clinical components — problem list, presenting problems, assessment & plan#299
Merged
Conversation
…, assessment & plan Implements the concern/assertion condition model from the design doc (Clinical-Med-Allergies-Conditions.md): - ProblemList: patient-level concerns with assertion history timelines (refine/revise/progress lineage), ICD-10/ICD-11/SNOMED codings shown parenthetically with full background in tooltips, uncertainty badges, observations (quick progress notes), and an Unconfirmed group separate from Active/Resolved - PresentingProblems: encounter-scoped relevant problem list fed by the patient problem list, with problem-focused vs comprehensive scope banner and relevance tagging on the encounter reference - Assessment: visit A&P with orders nested under problems via the durable concernId link, inline add-order, unlinked-orders bucket - ConditionEditor: add/observe/refine/revise/relate dialog with capture-first entry, coding rows, fuzzy onset, and three-state field uncertainty (unknown != blank, confidence levels) - MedicationList: presenting-medications reconciliation list - useDragReorder hook: shared HTML5 drag & drop reordering used by all lists (concerns, orders incl. cross-problem moves, medications), with full keyboard equivalents (Alt+arrows, Move menus, roving focus) and aria-live announcements for 508 compliance
…browser)
- scripts/codify/extract.mjs: dumps 770K MedicalCodify_search rows (12
codetypes) from the dockerized rxdb MariaDB to TSV
- scripts/codify/build-index.mjs: builds binary .mcdx index shards per
clinical domain (condition/med/lab/procedure/vaccine) with a sorted
token dictionary, posting lists, and curated alias expansion
(aliases.json: chf/lvhf, lasix<->furosemide, a1c/hba1c, brand<->generic)
- src/components/CodeLookup: shard parser + multi-word-prefix BM25-ish
engine ('con hea fa' -> Congestive heart failure) with edit-distance-1
typo fallback, Web Worker loader, and combobox component + stories
- Artifacts land in .storybook/public/codify/ (gitignored); pnpm
codify:extract && pnpm codify:build to regenerate
- Verified: golden queries return in 0.6-30 ms locally over 770K entries
Not exported from src/index.ts yet (worker bundling in tsup pending).
…down - Results now render in a floating dropdown (combobox pattern): opens on results, closes on select/Esc/blur, hover highlight without clobbering keyboard navigation (mousemove, not mouseenter) - Compact rows: label first, then codesystem + code right-aligned in small per-domain-tinted text (replaces the oversized badge) - Medication rows drill into all forms & strengths with ArrowRight (or the chevron): re-searches the med shard by name, filters to dosed FDB/RxNORM entries, alphabetized; ArrowLeft/Back returns. Aliases make Lasix drill list furosemide forms (114 entries) and vice versa - Selecting fills the input and suppresses the follow-up auto-search so the dropdown doesn't reopen
README covering the end-to-end design: extract/build pipeline, domain sharding, the .mcdx binary format, the prefix+BM25-ish scoring algorithm with alias and Damerau-Levenshtein-1 typo handling, worker loading, component keyboard model incl. med forms/strengths drill-down, and the production roadmap (RXCUI concept grouping, OPFS persistence, ranking priors, licensing). Linked from the Storybook docs.
- Add order now uses a code lookup instead of free text: new
renderOrderSearch prop (dependency-injected so the library build
doesn't bundle the lookup worker; story wires CodeLookup)
- Order type defaults to 'Auto': the type is inferred from the picked
code's system (RxNORM/FDB/NDC/CVX -> medication, LOINC/Quest/LabCorp
-> lab, HCPCS/ICD10PCS -> procedure); selecting an explicit type
filters the lookup to matching domains at query time
- CodeLookup: new searchDomains prop restricts searches per-query
without reloading shards
- Picked orders carry code {fullid, codetype, fullcode}; the story shows
it as the order detail
- Free-text entry remains the fallback when no lookup is provided
Dragging to select text in the embedded order search was hijacked by the draggable problem block. The block now sets draggable=false (and drops the grab cursor) while its add-order form is open, restoring normal text selection; drag & drop resumes when the form closes.
- New bare prop renders just the input + dropdown (no card, no status line) for embedding in forms; loading progress and error state show in the placeholder instead - Assessment add-order form uses bare mode: type filter, search, and Done now align at the same 40px height on one row
…s open The block body yields to text selection/editing (draggable=false), but the problem header row (number, name, codes) becomes the drag source, so the block can still be reordered mid-entry — the open form travels with it. The form itself is never inside a draggable ancestor.
New clearOnSelect prop, defaulting to true in bare mode: picking a result clears the query and keeps focus so the user can type the next order immediately. Standalone mode keeps the picked label in the input.
- New onAddAssessment prop + always-visible 'Add problem' search row (condition-domain code lookup): picking a dx adds a new problem to the assessment; the story creates the concern/assertion with the coding - Unlinked bucket now renders whenever adding is possible and gains an 'Add order' button opening the same type-filter + lookup form; onAddOrder's item param is null for unlinked orders (concernId unset), so they land in the bucket ready to be linked or dragged later
renderOrderSearch now receives a placeholder matched to the context:
'Search diagnoses… (try "chf"…)' for the add-problem row, and per
order-type hints ('Search medications… (try "lasix")', labs/a1c,
imaging/chest x, procedures, referrals) instead of one generic line.
The bottom row is now a single 'Add' search across all domains: picking a diagnosis (ICD10/ICD9/SNOMED US via new isConditionCodetype) adds a problem to the assessment, anything else becomes an (unlinked) order typed by its coding system. The unlinked bucket's add button is gone and the bucket only takes space once it actually contains orders.
- The unified add row gains a mode dropdown: Add (auto) / Add problem /
Add order. Problem/order modes scope the search domains and route the
pick directly; auto keeps codetype-based detection
- CodeLookup: new onFreeText prop - Enter with no highlighted result (or
the new 'Use "..." as free text' footer row) submits the raw text
- Free text in problem/order mode adds directly; in auto mode an inline
prompt asks 'Add "..." as: Problem / Order / Cancel' before adding.
Free-text problems arrive uncoded and unconfirmed in the story
- onAddAssessment now takes AssessmentAddPick { label, code? } so coded
and free-text problems share one callback
The toolbar actions previously only logged. They now open the editor seeded from the concern; saving appends the new assertion (revision refutes the prior) and repoints the visit item's assertionId so the block header, plan, and link chips update immediately.
Security / correctness: - extract.mjs no longer hardcodes the MariaDB password: reads RXDB_MYSQL_PASSWORD / MYSQL_PWD / --password, fails fast when missing, and passes it via the child environment instead of argv - engine.ts: viaAlias is now tracked per-document (aliasBuf scratch buffer, reset with the other buffers) instead of one shard-level flag - reorderIds() ignores dragged ids that aren't in the list (stale/cross- list payloads can no longer inject unknown ids) - useDragReorder keeps the dragged id in a ref set synchronously in dragstart (dragover no longer races React state) and clears the stale drop indicator when hovering invalid targets Accessibility: - CodeLookup ids are per-instance via React.useId (aria-controls / aria-activedescendant survive multiple instances); aria-expanded now mirrors actual dropdown visibility (incl. drill mode / free-text row) - Keyboard reordering (Alt+arrows + roving focus) added to MedicationList rows and PresentingProblems selected rows; Assessment unlinked orders now render via OrderRow (focusable + Move menu), replacing the pointer-only chip row - Pointer drag & drop now announces via the aria-live regions in ProblemList, MedicationList, and Assessment (reorder, move-to-problem) - ProblemList: Alt+arrow reordering and its aria-label hint are disabled in readOnly mode (canReorder follows drag.enabled) Styling / misc: - ProblemList timeline dot uses -left-5 (valid on Tailwind 3 + 4) - Drag indicator classes (opacity-40, inset shadows, cursor-grab) added to miewebUISafelist for Tailwind 3 consumers - ConditionEditor: seed effect depends on open/mode/prior (no stale form when switching while open); coding values are trimmed on save - PresentingProblems: unselected rows are only dimmed/badged out-of- scope in problem-focused encounters, not comprehensive ones - Prettier pass over all touched component files
…-fields file The MedicationListField import referenced a file that isn't part of this PR (removed in 923a7c1), which broke pnpm typecheck in CI.
- New useLiveAnnouncement hook: clears the aria-live region before setting the message (after a tick), so repeated identical announcements (e.g. two 'moved down' reorders in a row) are announced every time instead of being swallowed by React state bailout. Adopted by ProblemList, MedicationList, PresentingProblems, and Assessment - CodeLookup: domains/searchDomains now distinguish 'prop not provided' (undefined = all domains) from an explicit empty array (= none); the keys use null for absent and pass [] through to the worker - ConditionEditor: fields marked explicitly unknown no longer persist contradictory values on save - coding/severity/onset are cleared when their unknown toggle is set (unknown wins over a seeded value)
…, git-lfs shard distribution; move pipeline to packages/codify submodule
- Build pipeline moved to the new packages/codify submodule (also hosts
the SQLite FTS5 build + codify-mcp stdio server for agent loops)
- .mcdx v2: docPrior u8 section (log-quantized usage; simulated top-200
meds/ICD-10 diagnoses/SNOMED procedures until a production export is
wired in) + meta.locale; engine still reads v1 (prior=0)
- engine: final score x= (1 + 0.5*prior/255) - common codes outrank rare
ones at equal text relevance (hypertension -> I10 first)
- Shards now per locale ({indexUrl}/{locale}/); es is a curated sample
(common diagnoses + med ingredient INNs) with its own alias set (hta,
dm2, ic...); CodeLookup gains a locale prop
- Worker: OPFS persistence - network-first manifest check, cached shards
reused only when builtAt/version/bytes match, torn-cache-safe manifest
write-after, full offline fallback when the network is down
- Storybook: Language toolbar global (en/es) wired to CodeLookup stories;
shards committed via git-lfs and served from .storybook/public/codify/
(no longer gitignored)
The packages/codify submodule pointed at https://github.com/mieweb/codify.git, which does not exist on GitHub, so every CI job failed at the 'Checkout code' step (submodules: true). The codify data pipeline is build-time only and is not imported by src/, the tests, or CI. Remove the submodule and gitignore the local pipeline folder so checkout succeeds.
Contributor
There was a problem hiding this comment.
Pull request overview
This PR introduces a set of new Healthcare-focused, controlled (props-only) UI components implementing the concern/assertion condition model described in the included design doc, plus shared drag-and-drop reordering infrastructure and an offline CodeLookup proof-of-concept for Storybook.
Changes:
- Added new Healthcare components:
ProblemList,PresentingProblems,Assessment,ConditionEditor,MedicationList(+ Storybook stories). - Added shared hooks for accessibility announcements and HTML5 drag-reorder (
useLiveAnnouncement,useDragReorder) and safelisted required Tailwind classes. - Added offline
CodeLookupengine/worker/component + Storybook locale toolbar and committed CodeLookup shard manifests/LFS pointers.
Reviewed changes
Copilot reviewed 42 out of 43 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/tailwind-preset.ts | Safelists new drag/drop-related Tailwind utility class strings. |
| src/index.ts | Exports new Healthcare components from the package entrypoint. |
| src/hooks/useLiveAnnouncement.ts | Adds a hook for repeatable aria-live announcements. |
| src/hooks/useDragReorder.ts | Adds shared HTML5 drag-reorder hook + indicator class helper. |
| src/hooks/index.ts | Re-exports new hooks from the hooks barrel. |
| src/components/ProblemList/ProblemList.tsx | Implements patient-level concern/assertion problem list UI with grouping, history, reordering, and accessibility behaviors. |
| src/components/ProblemList/ProblemList.stories.tsx | Adds Storybook stories for ProblemList (interactive/read-only/empty). |
| src/components/ProblemList/index.ts | Barrel exports for ProblemList and its shared types/helpers. |
| src/components/PresentingProblems/PresentingProblems.tsx | Implements encounter-scoped “relevant problems” selection/tagging UI with scope gating and reorder support. |
| src/components/PresentingProblems/PresentingProblems.stories.tsx | Adds Storybook stories for PresentingProblems (problem-focused/comprehensive/read-only). |
| src/components/PresentingProblems/index.ts | Barrel exports for PresentingProblems and its types/constants. |
| src/components/MedicationList/MedicationList.tsx | Implements presenting-medications reconciliation UI with grouping, row actions, reorder support, and announcements. |
| src/components/MedicationList/MedicationList.stories.tsx | Adds Storybook stories for MedicationList (interactive/default/fully reconciled/read-only/flags/limited actions). |
| src/components/MedicationList/index.ts | Barrel exports for MedicationList and its types/constants. |
| src/components/Icons/index.ts | Adds additional lucide icon re-exports needed by new Healthcare components. |
| src/components/ConditionEditor/index.ts | Barrel exports for ConditionEditor and its types. |
| src/components/ConditionEditor/ConditionEditor.tsx | Adds a modal condition assertion editor supporting add/observe/refine/revise/relate and per-field uncertainty. |
| src/components/ConditionEditor/ConditionEditor.stories.tsx | Adds Storybook stories for ConditionEditor modes. |
| src/components/CodeLookup/README.md | Documents the CodeLookup architecture, build pipeline, and runtime behavior. |
| src/components/CodeLookup/index.ts | Barrel exports for CodeLookup component + engine utilities (but intentionally not from src/index.ts). |
| src/components/CodeLookup/engine.ts | Adds the shard parsing + local scoring/search implementation for CodeLookup. |
| src/components/CodeLookup/codify.worker.ts | Adds the worker that loads shards (with OPFS caching) and serves search results. |
| src/components/CodeLookup/CodeLookup.tsx | Adds the CodeLookup combobox UI that talks to the worker and supports med drill-down. |
| src/components/CodeLookup/CodeLookup.stories.tsx | Adds Storybook stories for CodeLookup across domains/locales. |
| src/components/Assessment/index.ts | Barrel exports for Assessment and related types/constants. |
| src/components/Assessment/Assessment.tsx | Implements visit-specific Assessment & Plan UI with nested orders and drag/link behaviors. |
| src/components/Assessment/Assessment.stories.tsx | Adds Storybook stories for Assessment (interactive/assessment-only/read-only). |
| package.json | Adds codify:* scripts to run external pipeline scripts under packages/codify. |
| eslint.config.js | Adds globals used by worker/binary/encoding code to satisfy lint. |
| Clinical-Med-Allergies-Conditions.md | Adds the design doc describing the canonical model and UI/projection plan. |
| .storybook/public/codify/es/med.mcdx | Adds LFS pointer for Spanish sample med shard. |
| .storybook/public/codify/es/manifest.json | Adds Spanish sample shard manifest metadata. |
| .storybook/public/codify/es/lab.mcdx | Adds LFS pointer for Spanish sample lab shard. |
| .storybook/public/codify/es/condition.mcdx | Adds LFS pointer for Spanish sample condition shard. |
| .storybook/public/codify/en/vaccine.mcdx | Adds LFS pointer for English vaccine shard. |
| .storybook/public/codify/en/procedure.mcdx | Adds LFS pointer for English procedure shard. |
| .storybook/public/codify/en/med.mcdx | Adds LFS pointer for English med shard. |
| .storybook/public/codify/en/manifest.json | Adds English shard manifest metadata. |
| .storybook/public/codify/en/lab.mcdx | Adds LFS pointer for English lab shard. |
| .storybook/public/codify/en/condition.mcdx | Adds LFS pointer for English condition shard. |
| .storybook/preview.tsx | Adds a Storybook global toolbar locale selector and default locale. |
| .gitignore | Ignores packages/codify/ and documents local/external pipeline expectations. |
| .gitattributes | Tracks .mcdx shard files via Git LFS. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…l codify repo to rebuild .mcdx shards
Deploying ui with
|
| Latest commit: |
fa67065
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://ded76c94.ui-6d0.pages.dev |
| Branch Preview URL: | https://clinical-components.ui-6d0.pages.dev |
… limit The 34 MB en/med.mcdx exceeded Cloudflare Pages' hard 25 MiB per-file cap, failing the deploy. Store it gzip-compressed (~11 MB) under the same filename; the worker detects the gzip magic (1f 8b, never collides with MCDX) and inflates transparently. Manifest bytes updated to the compressed size.
- Remove duplicate toolbarKeyNav re-export from ProblemList (single source in RowActionToolbar) to avoid conflicting star exports at the package root - Ignore Alt/Ctrl/Meta modifier combos in toolbarKeyNav so browser and AT shortcuts aren't intercepted - Add type="button" to RowIconButton to prevent accidental form submission - Add tags: ['autodocs'] to AllergyListField and MedicationListField stories - Fix sig grammar in MedicationList story sample data - Fix react/display-name in contextOrderSearch / contextConditionCodeSearch - Run prettier across src; exclude local artipod-sync from vitest
- Active relevance toggle: bg-primary-600 (3.28:1 with white text) -> bg-primary-800, matching the library convention for white-on-primary - Out-of-scope row dimming: opacity-60 blended muted text to ~3.1:1; opacity-80 keeps the dimmed affordance at ~5.2:1
- Link CodeLookup README docs to main instead of a feature branch - OrdersGridPlan: match actual export names (useOrderRowsUrl, ordersGridShared) - Add 'use client' to ordersGridShared (exports hooks/components) - Correct MedicationReconciliation actions JSDoc default
Comment on lines
+81
to
89
| // Drag & drop (useDragReorder / dragIndicatorClasses) | ||
| 'opacity-40', | ||
| 'cursor-grab', | ||
| 'active:cursor-grabbing', | ||
| 'shadow-[inset_0_2px_0_0_var(--color-primary-500,#3b82f6)]', | ||
| 'shadow-[inset_0_-2px_0_0_var(--color-primary-500,#3b82f6)]', | ||
| 'group-hover:opacity-100', | ||
| 'focus:opacity-100', | ||
| // Markdown SurveyBlock |
Comment on lines
+281
to
+282
| disabled={blocked.length > 0} | ||
| checked={checked.has(o)} |
…ctionToolbar variants - HealthSurveillance: disable checkboxes for already-pending orders so in-flight orders can't be re-submitted via 'Add N orders' (dup requests). - tailwind-preset: add RowActionToolbar reveal/overlay variants (pointer-fine, group/order, .wcdv-tr arbitrary variant) to miewebUISafelist so the toolbar works for TW3 consumers. Addresses PR #299 review comments.
…ions WebKit starts a text selection instead of an HTML5 drag when a draggable row contains selectable text (seen on iPadOS with a remote pointer). rowProps now sets user-select: none and -webkit-user-drag: element on drag rows so press-drag reliably initiates drag-and-drop.
Comment on lines
11
to
15
| 'dist/**', | ||
| 'packages/esheet/**', | ||
| 'artipod-sync/**', | ||
| 'tests/visual/**', | ||
| '**/*.spec.ts' |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements the concern/assertion condition model from the design doc (Clinical-Med-Allergies-Conditions.md) as a set of Healthcare components, plus the supporting drag-and-drop infrastructure.
Design doc
concernIdas the durable identity orders reference; time-stampedConditionAssertions carry the evolving ICD-10-CM / ICD-11 / SNOMED codings (refinement / revision / progression / reattribution lineage; revisions refute, never rewrite)IndicationLink= durableconcernId+ frozen coding snapshot (billing ICD-10 is a projection, never the join key)Components (props-only, controlled; each with Storybook stories under
Healthcare/)ProblemList— patient-level concerns grouped Unconfirmed / Active / Inactive / Resolved; expandable assertion timelines; parenthetical ICD codes with full coding/history/observations in tooltips; uncertainty badges; observations (quick progress notes); refine / revise / resolve / relate / observe actionsPresentingProblems— encounter-scoped relevant problem list fed by the patient problem list; scope banner; relevance tagging (Addressed / Relevant Hx / Noted); negative assertion gated to comprehensive scopeAssessment— visit A&P with orders nested under problems viaconcernId; inline add-order; unlinked-orders bucket with link chipsConditionEditor— add / observe / refine / revise / relate dialog; capture-first (name only required); coding rows; fuzzy onset; three-state field uncertainty (unknown ≠ blank, confidence levels)MedicationList— presenting-medications reconciliation listuseDragReorder— shared HTML5 DnD hook (no new deps, no localStorage; consumer persists id order)Accessibility (508)
Every drag interaction has a keyboard equivalent: Alt+↑/↓ reorder, Alt+←/→ move orders between problems, per-row "Move to…" menus, roving focus, toolbar arrow-key navigation, and
aria-liveannouncements.Not ready to merge — remaining work
AssessmentOrderis a display stub)toFhir,closeEncountermerge) per design doc phasesCI fix vs #297
packages/codifysubmodule (pointed athttps://github.com/mieweb/codify.git, which does not exist), soactions/checkoutwithsubmodules: trueno longer fails. The codify build pipeline is dev/build-time only and is not imported bysrc/, tests, or CI.